function LimeTrigger(videoId,opts) {
    //test if video is set
    if( videoId==undefined || $("#"+videoId).length==0) {console.error("video tag must be defined");return;}

    //default options
    var options = {
        interval:100
    }
    //merge options
    if ( opts ) {
        $.extend( options, opts );
    }

    //init player and return
    var player = new Trigger(videoId,options);
    return player;

    /*
     **************************************
     * The Player
     **************************************
     */
    function Trigger(videoId,options) {

        var video_element = $("#"+videoId);
        var video = video_element.get(0);

        var boundFunctions = [];

        //utils
        var utils = new Utils();

        //create Ticker and bind it
        var ticker = new Ticker(options.interval,video,boundFunctions);
        video_element.bind("play",function(){
            ticker.start();
        });
        video_element.bind("pause",function(){
            ticker.pause();
        });
        video_element.bind("ended",function(){
            ticker.pause();
        });
        video_element.bind('timeupdate',function(){
            ticker.step();
        });

        var clicker = new Clicker(video,video_element,utils,boundFunctions);
        video_element.bind("click",function(e){
            clicker.click(e);
        });
        $(window).resize(function() {
            clicker.init();
        });
        video_element.bind('loadeddata',function(){
            clicker.init();
        });

        this.bind = function(name,func) {
            if(boundFunctions[name]==undefined) {
                boundFunctions[name] = [];
            }
            boundFunctions[name].push(func);
        }

        this.unbind = function(name,func) {
            throw "unbinding is not implemented yet.feel free to fix it";
        }

        this.fire = function(name,e) {
            if(boundFunctions[name]==undefined) return;
            for(var i=0;i<boundFunctions[name].length;i++) {
                boundFunctions[name][i](e);
            }
        }

        this.getVideoSrc = function() {
            return video.currentSrc;
        }

        this.getVideoRegion = function() {
            var pos = utils.getAbsolutePosition(video);
            return {
                x:pos.x+clicker.getShiftLeft(),
                y:pos.y+clicker.getShiftTop(),
                w:Math.floor(video.videoWidth*clicker.getScale()),
                h:Math.floor(video.videoHeight*clicker.getScale())
            }
        }
    }

    /*
     **************************************
     * Ticker
     **************************************
     */
    function Ticker(interval,video,func) {
        var isRunning = false;

        function loop() {
            if(func["timeupdate"] != undefined) {
                //create event
                var e = {name:"timeupdate",currentTime:video.currentTime};
                for(var i=0; i<func["timeupdate"].length;i++) {
                    func["timeupdate"][i](e);
                }
            }
            if(isRunning) setTimeout(loop,interval);
        }

        this.start = function() {
            if(!isRunning) {
                isRunning = true;
                loop();
            }
        }

        this.step = function() {
            if(!isRunning) loop();
        }

        this.pause = function() {
            isRunning = false;
        }
    }

    /*
     **************************************
     * Clicker
     **************************************
     */
    function Clicker(video,video_canvas,utils,func) {

        var scale;
        var shiftLeft;
        var shiftTop;

        this.init = function() {
            scale = Math.min(video_canvas.width() / video.videoWidth, video_canvas.height() / video.videoHeight);
            shiftLeft = parseInt(Math.floor(video_canvas.width()-video.videoWidth*scale)/2);
            shiftTop = parseInt(Math.floor(video_canvas.height()-video.videoHeight*scale)/2);
        }

        this.click = function(click_e) {
            var p = utils.getAbsolutePosition(video);
            var e = {
                name: "click",
                x:  parseInt((click_e.pageX-(p.x+shiftLeft))/scale),
                y:  parseInt((click_e.pageY-(p.y+shiftTop))/scale),
                currentTime:video.currentTime
            }
            if(e.x <0 || e.x>video.videoWidth)return;
            if(e.y <0 || e.y>video.videoHeight)return;
            if(func["click"]!=undefined) {
                for(var i=0; i<func["click"].length;i++) {
                    func["click"][i](e);
                }
            }
        }

        this.getShiftLeft = function(){return shiftLeft};
        this.getShiftTop = function(){return shiftTop};
        this.getScale = function(){return scale};
    }


    /*
     **************************************
     * Utils
     **************************************
     */
    function Utils(video,video_canvas) {
        this.getAbsolutePosition = function(element) {

            var res = new Object();
            res.x = 0;
            res.y = 0;
            if (element !== null) {
                res.x = element.offsetLeft;

                var offsetParent = element.offsetParent;
                var offsetParentTagName = offsetParent != null ? offsetParent.tagName.toLowerCase() : "";

                res.y = element.offsetTop;

                var parentNode = element.parentNode;
                var borderWidth = null;

                while (offsetParent != null) {
                    res.x += offsetParent.offsetLeft;
                    res.y += offsetParent.offsetTop;

                    var parentTagName = offsetParent.tagName.toLowerCase();

                    if (parentTagName == "td") {
                        borderWidth = new Object();
                        res.left = 0;
                        res.top = 0;
                        res.right = 0;
                        res.bottom = 0;

                        var elStyle = window.getComputedStyle(offsetParent, null);
                        res.left = parseInt(elStyle.borderLeftWidth.slice(0, -2));
                        res.top = parseInt(elStyle.borderTopWidth.slice(0, -2));
                        res.right = parseInt(elStyle.borderRightWidth.slice(0, -2));
                        res.bottom = parseInt(elStyle.borderBottomWidth.slice(0, -2));
                        res.x += borderWidth.left;
                        res.y += borderWidth.top;
                    }

                    if (offsetParent != document.body && offsetParent != document.documentElement) {
                        res.x -= offsetParent.scrollLeft;
                        res.y -= offsetParent.scrollTop;
                    }


                    while (offsetParent != parentNode && parentNode !== null) {
                        res.x -= parentNode.scrollLeft;
                        res.y -= parentNode.scrollTop;
                        parentNode = parentNode.parentNode;
                    }

                    parentNode = offsetParent.parentNode;
                    offsetParent = offsetParent.offsetParent;
                }
            }
            return res;

        }
    }
}